'''
Created on 2013-02-04
'''

import pydot
import datetime
from uuid import UUID

import time

from core.himesis import HConstants as HC
from core.himesis import Himesis

from tc_python.arule import ARule
from tc_python.frule import FRule
from tc_python.lrule import LRule

from t_core.messages import Packet
from t_core.matcher import Matcher
from t_core.rewriter import Rewriter
from t_core.iterator import Iterator
from t_core.resolver import Resolver
from t_core.rollbacker import Rollbacker

#from symbolic_state_space import SymbolicStateSpace


from collapse_with_backward_patterns.HFindTwoMatchElementsSameTypeLHS import HFindTwoMatchElementsSameTypeLHS
from collapse_with_backward_patterns.HFindTwoApplyElementsWithBackwardLHS import HFindTwoApplyElementsWithBackwardLHS

from collapse_with_backward_patterns.HMoveOneInputDirectMatchDiffRulesLHS import HMoveOneInputDirectMatchDiffRulesLHS
from collapse_with_backward_patterns.HMoveOneInputDirectMatchDiffRulesRHS import HMoveOneInputDirectMatchDiffRulesRHS

from collapse_with_backward_patterns.HMoveOneInputIndirectMatchDiffRulesLHS import HMoveOneInputIndirectMatchDiffRulesLHS
from collapse_with_backward_patterns.HMoveOneInputIndirectMatchDiffRulesRHS import HMoveOneInputIndirectMatchDiffRulesRHS

from collapse_with_backward_patterns.HMoveOneOutputDirectMatchDiffRulesLHS import HMoveOneOutputDirectMatchDiffRulesLHS
from collapse_with_backward_patterns.HMoveOneOutputDirectMatchDiffRulesRHS import HMoveOneOutputDirectMatchDiffRulesRHS

from collapse_with_backward_patterns.HMoveOneOutputIndirectMatchDiffRulesLHS import HMoveOneOutputIndirectMatchDiffRulesLHS
from collapse_with_backward_patterns.HMoveOneOutputIndirectMatchDiffRulesRHS import HMoveOneOutputIndirectMatchDiffRulesRHS

from collapse_with_backward_patterns.HMoveOneInputDirectApplyDiffRulesLHS import HMoveOneInputDirectApplyDiffRulesLHS
from collapse_with_backward_patterns.HMoveOneInputDirectApplyDiffRulesRHS import HMoveOneInputDirectApplyDiffRulesRHS

from collapse_with_backward_patterns.HMoveOneOutputDirectApplyDiffRulesLHS import HMoveOneOutputDirectApplyDiffRulesLHS
from collapse_with_backward_patterns.HMoveOneOutputDirectApplyDiffRulesRHS import HMoveOneOutputDirectApplyDiffRulesRHS

from collapse_with_backward_patterns.HMoveOneBackwardLinkDiffRulesLHS import HMoveOneBackwardLinkDiffRulesLHS
from collapse_with_backward_patterns.HMoveOneBackwardLinkDiffRulesRHS import HMoveOneBackwardLinkDiffRulesRHS

from collapse_with_backward_patterns.HDeleteUncollapsedElementMatchDiffRulesLHS import HDeleteUncollapsedElementMatchDiffRulesLHS
from collapse_with_backward_patterns.HDeleteUncollapsedElementMatchDiffRulesRHS import HDeleteUncollapsedElementMatchDiffRulesRHS

from collapse_with_backward_patterns.HDeleteUncollapsedElementApplyDiffRulesLHS import HDeleteUncollapsedElementApplyDiffRulesLHS
from collapse_with_backward_patterns.HDeleteUncollapsedElementApplyDiffRulesRHS import HDeleteUncollapsedElementApplyDiffRulesRHS

from collapse_with_backward_patterns.HS2MS2Fexample import HS2MS2Fexample
from collapse_with_backward_patterns.HS2MS2FMatchexample import HS2MS2FMatchexample
from collapse_with_backward_patterns.HS2MS2F_example_newMM import HS2MS2F_example_newMM

from copy import deepcopy


# declare all the needed TCore rules (specific to the police station example)

find_elements_collapse_match = Matcher(HFindTwoMatchElementsSameTypeLHS())
find_elements_collapse_apply = Matcher(HFindTwoApplyElementsWithBackwardLHS())

move_input_direct_matchmodel = FRule(HMoveOneInputDirectMatchDiffRulesLHS(), HMoveOneInputDirectMatchDiffRulesRHS())
move_output_direct_matchmodel = FRule(HMoveOneOutputDirectMatchDiffRulesLHS(), HMoveOneOutputDirectMatchDiffRulesRHS())

move_input_indirect_matchmodel = FRule(HMoveOneInputIndirectMatchDiffRulesLHS(), HMoveOneInputIndirectMatchDiffRulesRHS())
move_output_indirect_matchmodel = FRule(HMoveOneOutputIndirectMatchDiffRulesLHS(), HMoveOneOutputIndirectMatchDiffRulesRHS())

move_input_direct_applymodel = FRule(HMoveOneInputDirectApplyDiffRulesLHS(), HMoveOneInputDirectApplyDiffRulesRHS())
move_output_direct_applymodel = FRule(HMoveOneOutputDirectApplyDiffRulesLHS(), HMoveOneOutputDirectApplyDiffRulesRHS())

move_backwardlink = FRule(HMoveOneBackwardLinkDiffRulesLHS(), HMoveOneBackwardLinkDiffRulesRHS())

delete_uncollapsed_element_match = FRule(HDeleteUncollapsedElementMatchDiffRulesLHS(), HDeleteUncollapsedElementMatchDiffRulesRHS())

delete_uncollapsed_element_apply = FRule(HDeleteUncollapsedElementApplyDiffRulesLHS(), HDeleteUncollapsedElementApplyDiffRulesRHS())



def graph_to_dot(name, g):
    vattr=''
    eattr = ''
    nodes = {}
    graph = pydot.Dot(name, graph_type='digraph')
    dateTag = datetime.datetime.now().strftime("%Y-%b-%d_%H-%M-%S")
    for v in g.vs:
        vattr += str(v['name'])                                                                                                                                                                                     
        vattr += ' < '
        vattr += str(v['mm__']) 
        vattr += ' < '        
        vattr += str(v['GUID__'])[-12:]
        print vattr     
        nodes[v.index] = pydot.Node(vattr)
        graph.add_node(nodes[v.index])
        vattr = ''
    for e in g.es:
        graph.add_edge(pydot.Edge(nodes[e.source],nodes[e.target]))
#    graph.write('/home/levi/Desktop/STATE%s%s.svg'%(key,dateTag))    
    graph.write('/home/levi/Desktop/%s.dot'%name)
#    
#    
#def merge_models(first, second):
#    nb_nodes_first = len(first.vs)
#    for index_v in range(len(second.vs)):
#        first.add_node()
#        first.vs[nb_nodes_first + index_v]['mm__'] = second.vs[index_v]['mm__']   
#        first.vs[nb_nodes_first + index_v]['name'] = second.vs[index_v]['name'] 
#    for index_e in range(len(second.es)):
#        first.add_edges((nb_nodes_first + second.es[index_e].tuple[0],nb_nodes_first + second.es[index_e].tuple[1]))
#    return first


def eliminate_repeated_collapse_matches(match_set):
    new_match_set = []
    for i in range(0, len(match_set)):
        found_repeated = False
        for j in range(0, i):
            if match_set[i]['1'] == match_set[j]['2'] and match_set[i]['2'] == match_set[j]['1']:
                found_repeated = True
                break
        if not found_repeated:
            new_match_set.append(match_set[i])
    return new_match_set

    
#def collapse(p, index):
#    
##    graph_to_dot('out' + str(index), p.graph)
#    
#    i = Iterator()
##    r = Rollbacker(HFindTwoStationsLHS())
#    p = find_elements_collapse.packet_in(p)
#    
##    print find_elements_collapse.is_success  
#
#    if True:
#
##        print 'Before:'
##        print p.match_sets[p.current].matches
##        p.match_sets[p.current].matches = eliminate_repeated_collapse_matches(p.match_sets[p.current].matches)
##        print 'After:'
##        print p.match_sets[p.current].matches        
##        print '------------------------------------'     
#        
#        p = i.packet_in(p)     
##        p = r.packet_in(p)             
#
##        m = ARule(HTestMatchLHS(), HTestMatchRHS())
##        m = Matcher(HTestMatchLHS())
#
#        while True:
#            
##            print '-----> Depth: ' + str(index)
##            print p
##            print p.match_sets[p.current].match2rewrite
#    
#            # perform one collapse step
#
##            p = rin_match_direct.packet_in(p)
#
##            p = find_elements_collapse.packet_in(p)
#            p = rin_match_direct.packet_in(p)
#            p = rin_match_indirect.packet_in(p)
#            p = rout_match_direct.packet_in(p)         
#            p = rout_match_indirect.packet_in(p)
#            p = erase_match_collapse_element.packet_in(p)
#            
#            print 'In:' + str(erase_match_collapse_element.is_success)
#            
#            print p
#            
#            graph_to_dot('out', p.graph)            
#            
#            break
#            
##            p = rout_match_direct.packet_in(p)
##            p = erase_match.packet_in(p)    
##            
###            graph_to_dot('out' + str(index) + '.' + str(i.iterations), p.graph)
##            
##            p1 = Packet()
##            p1.graph = deepcopy(p.graph)
##            
##            # collapse the remaining graph   
##            collapse(p1, index+1)            
##    
##            # get back the graph previously to the collapse step
##            p = r.next_in(p)
##            # iterate to the next two elements to collapse
##            p = i.next_in(p)
##            if not i.is_success:
##                return True
##            
## #           print p
##
##            # save the state of the iterator before collapsing
##            p = r.packet_in(p)
#        
#    else:
#        return False


def collapse_backward(p):
    '''
    Collapse two elements of the match part of the state.
    
    If those two elements are backward linked to two elements on
    the apply part and those two apply elements share the same
    type, then they will also be merged.
    '''
    
#    graph_to_dot('out' + str(index), p.graph)
    
    i = Iterator()
#    r = Rollbacker(HFindTwoStationsLHS())
    p = find_elements_collapse_match.packet_in(p)
        
    p = i.packet_in(p)    
#    
    p = move_input_direct_matchmodel.packet_in(p)    
    p = move_output_direct_matchmodel.packet_in(p)
    p = move_input_indirect_matchmodel.packet_in(p) 
    p = move_output_indirect_matchmodel.packet_in(p)
    p = move_backwardlink.packet_in(p)
    p = delete_uncollapsed_element_match.packet_in(p)

#    graph_to_dot('out', p.graph)      
    
    p1 = Packet()
    p1.graph = deepcopy(p.graph)
    
    p1 = find_elements_collapse_apply.packet_in(p1)

    print 'Result:' + str(find_elements_collapse_apply.is_success)   
    print '--------------------------------------------------'

    i2 = Iterator()
    p1 = i2.packet_in(p1)        
    
    if find_elements_collapse_apply.is_success:
        p1 = move_input_direct_applymodel.packet_in(p1)
        p1 = move_output_direct_applymodel.packet_in(p1)
        p1 = delete_uncollapsed_element_apply.packet_in(p1)
        print 'Result:' + str(move_output_direct_applymodel.is_success)   

#    print 'Result:' + str(delete_uncollapsed_element_apply.is_success)

    graph_to_dot('out', p1.graph)

    if find_elements_collapse_match.is_success:
        return [p1.graph]
    
    return []


if __name__ == '__main__':                                                                                                                              
    pass

                                                                                                                                                                     
#find_elements_collapse_match = Matcher(HFindTwoMatchElementsSameTypeLHS())
#find_elements_collapse_apply = Matcher(HFindTwoApplyElementsWithBackwardLHS())
#
#move_input_direct_matchmodel = FRule(HMoveOneInputDirectMatchDiffRulesLHS(), HMoveOneInputDirectMatchDiffRulesRHS())
#move_output_direct_matchmodel = FRule(HMoveOneOutputDirectMatchDiffRulesLHS(), HMoveOneOutputDirectMatchDiffRulesRHS())
#
#move_input_indirect_matchmodel = FRule(HMoveOneInputIndirectMatchDiffRulesLHS(), HMoveOneInputIndirectMatchDiffRulesRHS())
#move_output_indirect_matchmodel = FRule(HMoveOneOutputIndirectMatchDiffRulesLHS(), HMoveOneOutputIndirectMatchDiffRulesRHS())
#
#move_input_direct_applymodel = ARule(HMoveOneInputDirectApplyDiffRulesLHS(), HMoveOneInputDirectApplyDiffRulesRHS())
#move_output_direct_applymodel = ARule(HMoveOneOutputDirectApplyDiffRulesLHS(), HMoveOneOutputDirectApplyDiffRulesRHS())
#
#move_backwardlink = FRule(HMoveOneBackwardLinkDiffRulesLHS(), HMoveOneBackwardLinkDiffRulesRHS())
#
#delete_uncollapsed_element_match = FRule(HDeleteUncollapsedElementMatchDiffRulesLHS(), HDeleteUncollapsedElementMatchDiffRulesRHS())
#
#delete_uncollapsed_element_apply = ARule(HDeleteUncollapsedElementApplyDiffRulesLHS(), HDeleteUncollapsedElementApplyDiffRulesRHS())

p = Packet()

#p.graph = HS2MS2Fexample()
#p.graph = HS2MS2FMatchexample()
p.graph = HS2MS2F_example_newMM()

graph_to_dot('source', p.graph)

#collapse(p, 1)

t0 = time.time()

collapse_backward(p)

t1 = time.time()

print '\n'
print '-----------------------------------------------'      
print 'Done in: ' + str(t1 -t0)
